{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "785c5543",
   "metadata": {},
   "source": [
    "## MTGNN with Linear Prediction and Reconstruction\n",
    "The MTGNN model is used to project the input data into a feature space. <br />\n",
    "From the output two linear convolution models are trained for <br />\n",
    "prediction and reconstruction."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "59b585d9",
   "metadata": {},
   "source": [
    "### MTGNN P+R"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "7e473b3a",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 500/500 [29:23<00:00,  3.53s/it]\n"
     ]
    },
    {
     "data": {
      "image/png": "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rJ6OUhQ6docJIaClRdLa6upucRQKRQc45JBDyMvL47vvvmPWrFkcf/zxjB07FqfTyTXXXMO0adOYOHEiTzzxBKBlAV1yySWMGjWKI488kj179nj2NWvWLJYuXQrAZ599xn777cekSZM44ogjyM/P5/HHH+f+++9n8uTJfP/999xyyy3ce++9AKxcuZIDDjiAiRMnctJJJ1FRUeHZ59/+9jemT5/OyJEj+f773uHq3ndyx9pACIHNYaKpwUVDXSsJSbbuFkmh6FUMF+2a4hoxefK0iLZrbW3l008/Zfbs2QCsWLGCNWvWkJuby5NPPklSUhJLliyhqamJgw46iKOPPpoVK1awceNG1q1bR3FxMWPHjuW8887z2W9JSQkXXHABixYtIjc3l/LyclJTU1mwYAHx8fFcffXVAHz99deez5x99tk89NBDzJw5k5tvvplbb72V//73vx45f/31Vz755BNuvfVWvvrqq074lqKLsigM2O3a11FXq9xPCkVvoaGhgcmTJzN16lQGDRrE+eefD8C0adM8tQJffPEFL7zwApMnT2bGjBmUlZWxefNmFi1axBlnnIHZbKZ///4cfvjhAfv/+eefOfTQQz37aqs7a1VVFZWVlcycOROAP//5zyxatMjz/sknnwzA/vvvT35+/l6ff1egLAoD9hgTVEJDnbO7RVEoeh2R3vl3Nu4YhT9xcXGe11JKHnroIY455hifbT755JNoixeA3W4HtCB8R+MnXY2yKAzYPBZFSzdLolAoOpNjjjmGxx57jJYW7X9706ZN1NXVceihh/L666/jdDopLCzkm2++CfjsAQccwKJFi9i2bRvgnfuQkJBATU1NwPZJSUmkpKR44g8vvviix7rorSiLwoA9Rst8UhaFQtG3mDdvHvn5+ey3335IKcnIyOC9997jpJNOYuHChYwdO5ZBgwZx4IEHBnw2IyODJ598kpNPPhmXy0W/fv348ssvmTNnDqeccgrvv/8+Dz30kM9nnn/+eRYsWEB9fT1Dhw7l2Wef7apTjQpCStndMnQ6U6dOle5shUgpLy/nnCN/Zt2KGp7+5FBm/j47StL1HNQUsH2DaJ7z+vXrGTNmTKfvd29RvZ7CE+LvFrJku8d/i0KIOOBRoBn4Vkr5crSO5YjVLAoVzFYoFAov3RKjEEI8I4TYI4RY47d+thBioxAiTwhxnb76ZOAtKeUFwPHRlMseo30dDXVKUSgUCoWb7rIongMeBjz17kIIM/AIcBRQACwRQnwA5ACr9c0iCh60trZ6zO1IqaysxGLTdl9SXNXuz/dGKisru1uELkedc+ficrl6ZOaO07nvxRnbc84ulyvgGhfONdktFoWUchHgfyWeDuRJKbdKKZuB14AT0JRGjr5NSHmFEPOFEEuFEEvLyso6JJfDY1Hsez8yhUKhCEVPilEMAHYalguAGcCDwMNCiD8AH4b6sJTySeBJ0ILZHQncJSTFABU4W637VLBzXzpXN+qcO4fi4uIeHTDuybJFi0jO2WQytev30OO/RSllHXBuVxwrPlH7OspLmrricAqFQtEr6EkFd7uAgYblHH1dl5GYrCmK0qKeNf9XoVCExthmfM6cOd0ah/LvKLu3vPfeez6tzm+++eZu6Q3VkxTFEmCEECJXCGEDTgc+6KqD15c00/jwTv4AlCmLQqHoNRjbjKempvLII490myzhFEVHgv7+iuK2227jyCOP7LB8HaW70mNfBX4CRgkhCoQQ50spW4FLgM+B9cAbUsq1XSaUlDhLW0gAykuURaFQ9EYOPPBAdu3SHBFbtmxh9uzZ7L///hxyyCFs2LAB0OIqJ510EpMmTWLSpEmeC/t//vMfxo8fz/jx4z2dXvPz8xkzZgwXXHAB48aN4+ijj6ahoQGABx98kLFjxzJx4kROP/30oK3HzznnHBYsWMCMGTO49tprfVqRA4wfP97TGPCFF15g4sSJTJo0iT/96U8sXryYDz74gGuuuYbJkyezZcsWn8FKX3/9NVOmTGHChAmcd955NDVpN7hDhgzhH//4B/vttx8TJkzwnPfe0C0xCinlGSHWfwJ0uEuXEGIOMGf48OHt/6xZK0oUQEWpsigUivZyR5TajN8YYbNBp9PJ119/7ekee9FFF/HEE08wYsQIfvnlFy6++GIWLlzIZZddxsyZM3n33XdxOp3U1taybNkynn32WX755ReklMyYMYOZM2eSkpLC5s2befXVV/nf//7Hqaeeyttvv81ZZ53FnXfeybZt27Db7VRWVpKcnBzQevzpp5+moKCAxYsXYzabueWWW4LKvnbtWm6//XYWL15Menq6p5X58ccfz3HHHccpp5zis31jYyPnnHMOX3/9NSNHjuTss8/m8ccf5/LLLwcgPT2d5cuX8+ijj3Lvvffy1FNPdfDb1+hJrqe9Rkr5oZRyflJSUrs/a7JoisIE1Fa30tysUmQVit6Au814VlYWxcXFHHXUUdTW1vLTTz8xd+5cJk+ezIUXXkhhYSEACxcu5KKLLgK0+EZSUhI//PADJ510EnFxccTHx3PyySd7mvrl5uYyefJkwLc1+MSJEznzzDN56aWXwmYazZ07F7PZHPYcFi5cyNy5c0lPTwfazlDbuHEjubm5jBw5EtBamf/www+e9zu7lXmPz3rqKoTJa1GAFtDuPygu9AcUCoUPkd75dzbuGEV9fT3HHHMMjzzyCGeddRbJyclB24+3F3dbcNAUi9v19PHHH7No0SI+/PBD7rjjDlavXh3088Z25xaLBZfLO0GzsTE6bu7ObmXepyyKvcFoUQAs/rq4+4RRKBTtJjY2lgcffJD77ruP2NhYhgwZwptvvglo8yhWrVoFwBFHHMFjjz0GaO6qqqoqDjnkEN577z3q6+upq6vj3Xff5ZBDDgl5LJfLxc6dOznssMO46667qKqqora2NmTrcTdDhgxh+fLlACxfvtzTuvzwww/nzTffxF0s3FYr81GjRpGfn09eXh6gtTIPJ+/eohSFjjtGoRsWfPLGzjBbKxSKnsiUKVOYOHEir732Gi+88AJPP/00kyZNYty4cbz//vsAPPDAA3zzzTdMmDCB/fffn3Xr1rHffvtxzjnnMH36dGbMmMG8efOYMmVKyOM4nU7OOussJkyYwJQpU7jssstITk5mzpw5vPvuu55gtj//93//R3l5OePGjePhhx/2uI7GjRvHjTfeyMyZM5k0aRJXXnklAKeffjr33HMPU6ZMYcuWLZ79OBwOnn32WebOncuECRMwmUxceOGFnflV+qDajOuUlZbxeIaWn/wsYLWZWFp2InHx1ihI2DNQLbf3DVSb8X2DaLYZVxaFjjtGATB4WBwtzS5+WrinGyVSKBSKnoFSFEb0byN3ZAIA+ZtC+xoVCoViX6FPKQohxBwhxJNVVVUd+7xuVWT00zIGdmyp7TTZFIq+Sl90X/dlOvL36lOKYm/qKACE/m2kpWuK4pXHt3Di1C+oq23pLBEVij6Fw+GgrKxMKYtegpSSsrIyHA5Huz6370R6IkEPU6Sm2Tyr1iyrYPHXezjqhAHdJJRC0XPJycmhoKCAkpKS7hbFB3etgsnUp+6FwxLpOTscDnJycsJu449SFAY015MkNc3usz5vbZVSFApFEKxWK7m5ud0tRgAqu61z2XfUbSTo34bdYebMi4d5Vq9ZXtFNAikUCkX3oxSFAU+KrEty6Oxsbn5QK7hZ9kOp8sEqFIp9FqUojOjfhnRpSqH/4FgSU6yUFjeybmVl98mlUCgU3YhSFAbcFoXUG8cKIRi/fwoAP3xR1F1iKRQKRbfSpxTF3tdRaM9uiwJg1IRkAO657jc+f0f1f1IoFPsefUpR7G0dhacjoNOrKIaNSfS8vuvaVXsjnkKhUPRK+pSi2FuCWRTpmXbSs7TilKoKVXinUCj2PZSiMOCNUXgVhRCCfz62P1aroKq8maqK5u4ST6FQKLoFpSiMeCwKv9VmQdbAWAB++VZ1lFUoFPsWSlEYCGZRuJkxKwOAlx7N61KZFAqFortRisKI+9twBSqKAw7LBGDp9yU0Nzu7UCiFQqHoXpSiMBDOokhIstKvv4PmJhdrVUsPhUKxD6EUhYFgWU9Gho/V0m4f//d6Hv7nWpxOV9DtFAqFoi+hFIUBYQ5tUQBMOyQdgK8/2M1/b17Diw+reIVCoej7hFUUQgizEOJ4IcSorhJob9jbymxvjCL426MnJROX4O3MvurXso4dR6FQKHoRYRWFlNIJPA0c2DXi7B17P+FOtyhCuJ5MJkFGlncyVGWZqqlQKBR9n0gGF70MnCOEWAIUuldKKcujJlU3ES6Y7cYRY/a83p6nZmorFIq+TySK4jJAAr8Z1skIP9uraCuYDWC1eY2wHVtqKSlqICMrJtqiKRQKRbcRSTB7UZDH99EUqtswewcXhWLkBF+31o9fFUdTIoVCoeh22rQKpJSzukCOHkEkrqfDj+uPySTYvaOeH78sZuXPZZx41pAuklChUCi6njYtCiFEkhDiOSFEsf54RgjRwT7ePRsRoteTEYvVxJEnDGDKgWkAbFhVGX3BFAqFohuJxPX0IHA20Kw/zgH+Gz2Ruo+26iiMDBgcB0De+uqoyqRQKBTdTSSK4vfA3VLKgVLKgcA9wB+iK1Y3YWo7RuEmJd2GI8ZMZVkzu3fURVkwhUKh6D46Upnd9lW0lyL0zNdILAohBKMnaR64T95QI1IVCkXfJRJF8QlwjRBihxBiB3AN8HF0xeoehAhfcOfP9EP7AfDNx4VtbKlQKBS9l0gUxV/Riu5i9MeLwBVRlKnD7G0Lj/ZYFADZg7RhRsr1pFAo+jJt9noC/g48K6XM0B/nSCkru0S6dtJZLTxC9XryJzXdBkDxrgak7LMeOYVCsY8TSa+nE4FhXSJNd9NOi8IRa8ERa6a5yUV5aVMUBVMoFIruIxLX07fAzUKIvwghTnY/oixXt9BWU8BgJCRZAfjHxcuUVaFQKPokkfRrOld/flB/FmiZT+bgm/dePAV3EVoUAJn9YygpbOSztwqY+7uvefnbw7Db+9xXo1Ao9mEiURS3Rl2KnoK5/RbFnD8OYs/uBvYUNrLy5zI2rKpk0vS0aEmoUCgUXU5YRaEHsxOBj6SU33SNSN1HJL2e/BkyIoF/PjGVh25dy5plFbz1zDYa6p0cMKtftMRUKBSKLkUFsw2YrJqicDW3fxZ2v2xtoNGrT2zhrMO+UfO0FQpFnyES19O3aMFsO76Di96JllDdhUmfNeFqdLb7s2mZDp/litJm0v3WKRQKRW9EBbMNmOyaReFsbL814K8U9hQ2KEWhUCj6BJEoitvow/2djJjsHbcockcmYI8x09SgfbakqLFTZVMoFIruIpLBRbd0gRw9ArfrqSMWRVKqjf+8NIPnHtjEkkWllBQ2dLZ4CoVC0S2EDGYLIZYLIY4SQsTpw4pG6+tPEkKUd52IXYfJpgezm9pvUYA21CglzQ4oi0KhUPQdwmU9TQZSAAfasKL++nob0Ccn3HmC2U0dz1hKStX6PxUVKItCoVD0Ddpq4dGrYhN72z3WE6PooEUBkNZPsyjyN9V0eB8KhULRk2hLUZwH/BtNYVwihHgQzbrokext91iv66njFkXmAK31eP5mpSgUCkXfoK1g9jGG1ycaXvcqSyNSPBZFBwru3GRkOxACCnfW09TkVH2fFApFryecojisy6ToIXRGjMJqNZGSbqe8pIlvPtpNYrINIeDAwzM7S0yFQqHoUkIqCinld10pSE/A43pqdiGl9IxGbS+Z/R2UlzRxySmLPeveX34046akdIqcCoVC0ZVEMo9in0GYhOcb2Rv3U9bA2IB1m1ZXdnh/CoVC0Z0oReGHsOy9+ykrJ1BR1FS1dHh/CoVC0Z0oReGH0GdSdKSNh5tkvZYC4NhTcwBVgKdQKHovIWMUQoibw31QSnlb54vT/QiL3hiwoeOKYuyUZIaNTWTMxCQSUzSlsadQKQqFQtE7CZf1dIvhtUTrGut+DVqzwD6HsOr9nuo7rihsdjPX3jkRgOWLSwEoLVaKQqFQ9E7CKYq5+vNhwEzgfjRX1eXAT1GWq9sQtr1XFEYSkzWLQikKhULRWwmXHvs2gBDiXuAOKeUz+rIAru0a8boeYd1715ORxGQrAKUqRqFQKHopkcyjsAP/EELkoFkU59KHg+CeVuP1rZ2yv9QMO0JA8a4Gjhz1MfOuGsXp84d3yr4VCoWiK4jkgn81kAbcDNykv74qmkJ1J54YRSdZFMbW4/mbarnpwmWdsl+FQqHoKtpUFFLKV4AhaL2eTgSGSClfi6pU3Yg7RtHaSRYFaP2fjLz5zFaczo7XaSgUCkVXEqkLaRpwOJAHHC2EmBQ9kboXj0VR23mKIisnxmf5+vOX8Oz9mzpt/wqFQhFN2lQUQoi/Ah8ClwJZwMnAPdEVq/vwBLM7KesJYOSEwLbnm9dVd9r+FQqFIppEYlH8FXjTsPwVsF9UpOkBuC2K1rrOsygmTkv1vD7rL1ogu3hXfaftX6FQKKJJJFlPKcAq4BR9ORbokUMWhBBzgDnDh3c8q6iz6yhAK8C7/r5J1Ne2EhOrfeV5a6tpanRid/TIr1KhUCg8RGJR/ApcpL++Gi376eeoSbQX7O2EOwBTJ9dRuBkyIoGxU1JIStXqKop2NTAr9yOKd6vZ2gqFomcTiaK4FGhAa+ExGyhEc0f1STqjhUc4EpK9DQNLihr55I0dUTmOQqFQdBZhXU9CCDMwEi2A7c7n3CiljM5VtAfQ2XUU/litvrp5w29VUTmOQqFQdBZhLQpdITwNTJVSrtMffVZJgDfraW/ajLfF/gene16rgUYKhaKnE4nr6WXgHCHEOCFEqvsRbcG6C6NFIaVsY+uOMf/a0dzz/HQAVi+t4K6/rYzasRQKhWJviURRXAYcAvwGlOiPPdEUqjsRJqHNpJB7N+WuLRJTbCSlaIHt/929kRU/leF0urj01MU8/6AqxlMoFD2HSNJjF+GdQbFPYLKZcLY6cdY7MUcxfTWtn4OqCm1EallJE0t/KOXTN3fy6Zs7+fNlI6N2XIVCoWgPkfR6miWlPMz/0RXCdRed3UE2FKdeMNTzuqSwgdYWrwXT2BDdYysUCkWktGlR6PMnTgcmAO7udlJK2Wc7yHoURZQyn9zkjkzg2FNz+OSNAvbsbiAl3e55ryC/nuFjEqN6fIVCoYiESFxPjwALCByH2ncVhT26tRRGklI15VC0q4HMAd7mgQXbahk+JhEpJVKCySRC7UKhUCiiSiTB7JOAV/TXlwPfAP+MmkQ9AI+i6MR+T6FwB7T37GqgoqzZs37H1joALj11MYcP+5imKKbrKhQKRTgiURQpwPf660LgLWB+1CTqAZjjNEOruaK5jS33nmR9qFFhQT3lJU2e9T8tLKaxoZXP3iqgIL+O5YtLoy6LQqFQBCMS11ORvl0R8BRgA/p0j2xbmtZmo6Eg+n2YsvVZFZvXVrN5rfdr/fLdXVx4wg+e5YYucIMpFApFMCKxKG4CtqDFJBqBKvpwrycAux5UbiiIfitwR6yFmLjgKbg/flnseV1b3RJ1WRQKhSIYbVoUUsqXDIt9dgSqEWuqZlE07m7skuOlZzrYqcckQvH5OwUMG5NITKyZzAExxMVbu0Q2hUKhiCQ9dmGQ1VJKeUQU5OkR2FJsIKC5tAlXiwuTNRLDq+OcsWAYn71VwJEn9Oe3X8v56v3dAdt8/nYBn79dAMDwsYl8tvb3UZVJoVAo3EQSo5gVZF2frtQWZoEl0UprVQuNhY3EDoqN6vGGjU7kLzeNBSA+0RpUURjJU2NUFQpFFxKJosgwvE4BbkHLfurT2FJtmqLYVR91RWEkNj6SP4lCoVB0HZH4VKThUQ1sBP4cTaF6ArZ0PfOpi+IUbuKUolAoFD2MSK5KpQS6mjZGQZYehT1Dy3yq21Lbpce12gJ198TpqTQ3OdmwSg05UigUXU97u8c6gXzg3mgJ1FNw9NfqG+o21XTpcYUQ7Pe7NPI313oK8Gw2E44Y3xRal0uqth4KhaJLiCQ9dlYXyNHjsPezIyyCpj1NtFS3YE3sunTUC68bg5SSBSf8CMCewgZGTUj22aa+rpX4BJUiq1Aook8k6bHPhHlbSinP70R5egzCJLCl2WgqbqJxV0OXKgrQLIvEZCvVlS3EJVhJy7T7vP/ak1uYd9XoLpVJoVDsm0TiejqHwM6xxtd9UlEAWBKtNBU30VwW/Z5Pwbj+vkm899J2jv/jYOITrZjNgpcf3QLAnVev4vT5w4iLt6B1go+c8tIm4hK8f/qyPY2cNO1Lzrx4OBf+bUynnoNCoej9RJL1dC9aU8AjgaP11/cB04Dp0ROt+3FbEc1lTW1sGR1SMxycd8Uo0jMdOGLMHDo72yfYfd/1vzEl+R2WfF8S8T7LShqZnvEes8d+6ln3zP2b2L2jnnuu+61T5VcoFH2DSBTF2cDrUsqFUsqvgDeAU6WUy6SUy6IrXvsQQswRQjxZVdU52UGWJE1RNJV0j6IIRkuzdwrei4/kUVvdyl/+78eg2y79oYRLT/2RshJviu/qJeUAPi1DXM4+XT+pUCj2kkhcTw3Av4UQB6C5nI4HyqIqVQeRUn4IfDh16tQLOmN/1kTt6+lJiiIY5SHkO/0QrftKXLyVO5/RjD+zJbrtSBQKRd8jkqvGPDRl8SfgLKBeX9fnsehZRc09XFG0ReFObxdck+Ev7nIpS0KhULRNm4pCSvk1MBiYrD+GSCm/ia5YPQPPXIqd9cgeclFNzbAHXb/qV18j77kHvDWR0iB6c5PXddVQp2ZcKBSKtgmrKISeTiOlbAaygaOAmV0gV4/AEm/BHGfG1eSiqahrW3mE4srbx3Po77MC1t/+1xXccsky9hQ26MsrPe+5DJrC2FCwvlYb9Splz1CCCoWiZxJSUQghvga+1F+fD3wC3A18JoS4qWvE637s/RwA1HZxK49QZGTHcPr8YfhnxK74qYyXHsnjzqtXUuGfpaXrgS/f38Vd167yrK6rURZFV6IUsqK3Es6iGA98rL9eoD//E/gO6JRgcW8gdojWOTbvno3U7wg/XKirMJsFlhAzMnbvqGfbRt+2I+4L1P/uXu+z3m1RKKLP3xcs5eCBH1JboyYVKnof4RRFElAmhEgCpgA7pJS3AM8D/bpAth5B4oQkz+tterFbT8DuCP6ns1hNbNngO6/CpYcl/GMStTVu11Po49TVtvDVB7v48v1dlO3pGe633sirT2yheFcDCz8MP2tEoeiJhFMU+Whzsl/St/tMXz+IHpoeGw2siVYyj9ViAjXrqmkq6RkXy7MvHYHZIpgwNcVn/c/f7OHGC5b4rGttcXHzxUtZv6rSZ319raY4jIF6f/fII7evY8EJP3DRiT/wz8tXdOIZ7JuoTDNFbyScovg7MAr4A1qr8fv09acDP0dZrh5F8n4pxA6LA6Bmbc+YLjdpRhr3PD+di24Yg9niG7BwuXy3XfFTGa88FmgN1ekWRbOhiM/4GuD5BzZ5Xn/02o69FXufp6dkzykU7SGkopBSvgkMAGYAQ6WUeUIIC/BH4C9dJF+PIWaA1na8dnPPCGoDxCVYMVtM3PifyZx2QW67P+9WFE2NXpeUMX0WID7J2wwxd2RCByVVuPFX4gpFbyBseqyUskxKuURKWasvt0opV0kpi7tGvJ6Dez5F7eaunU8RCQOGxHH4nAFMnJ4KQPbAWKbPzGjjU1BTpSuKBoOiMCiNyvImyoq9GVRVFVpzxJYWFzu39RyF2ZtQridFb0T1c4gQ98Q7dz2FdElqNlSz8d/rKf+1Z4Rs/nLTWB5//yBueWS/oIV5Nr8AeHmpduFvMlgRj9y+zvN6s+5my8rRlGRVRTNSSi455UcOG/oxP35VFFKWlhYXvy0pw+lUt9BGlKJQ9EaUoogQiz7LuqWqBemUFL63i7XX/kbFT2Vsun19G5/uOtwtx4NNvztt3lCf5UpdUTQ3ea2IFx7azPnHfsc7z29j0xqtueKgYfHYHSacrZLqyha+/kDL3Hn7ufyQcvzryhWcPP0rHvtXz/luegJS6U1FL0QpiggRZoE51gwSmiua2RHmItkTGKDXfxgxmX2Vx5fvFrNmaRVNDb5Xr+8+LeLac35l05pKAHJy44jT+15VlHpdUcHGYCxfXMpPC4t58eE8AJ777yZVaGZAWRSK3kgkE+5GAVcDQwD34GYppTwiinL1SMzxFpz1TjYa3DM9lf0PSqd2QSsjxiZy5zWraG5yMWhoPCaTb0D1vGN+ZerB6UH3sXZ5JQADBscRl2ChvKTJp1NtMEVx6kFf+yxXljdzYPb7vP3LUQwYHLfX59XbUYpC0RuJxKJ4D22K3RHALMNjn8OiT4Wr3xpYob3ny2KaSpoo+mg3rtbu9y8IIZh1bDYDhsTxz8f359q7JpKTG8cd/5vGGQuG+WzrX1/hZuXPWuyl/6BY0jO1Viab1oae9dHSEvy8S4ubePq+jUHf29dQxpWiNxKJokgF7kdrCpihP/aZymwj1qTQc7O3PrSZjXesI//Jrex4Pr/rhIqA5DQ7w8YkAlr32QMP9/3zudNkg2F3mElJtzFqolah/sMX3gC2/0WvoS70fvxrPfZVVB2FojcSiaJ4ARgOxKO1l3M/9jmS909BGEaRxgzyjQO4LY3ShXu6VK72YneY295IRwjNOhk9MRmAz98u8LxXU+Xbt6guTO8oKdlnW4AY3U2trfvkv46ilxOJorgKOA7YBJToj559JYwSjkwHw68c4Vm2JAUP8bTqd+hlP5VS8k3PLDk5/Pj+EW130JGa9ZGVE8OgYXE+VkRNZbPPtuEsk+f+u4kZme9TXtq7h0B1BKdh1GxrCPecQtGTiWQU6iL2UQsiGCaLieSpKVStqCTtd+nUrKkO+u201ray+d8bAEidkYY5NpKvuus4bd5Q5pyZSkOdixvOCwzOX3jdaBKSrAwcGg9oVsX0QzPYscUbn6n2syjqa9vujLp6aTkzZ2fvpfS9C6chZtXSrBSFovfR5tVLSjmrC+ToVfQ7OpP0WRmYHWYGnTuEHc/kB2xTn++9oLZUt/Y4RQHaxT823sx9L81g2Y+l/LaknDVLKwDNVTRiXJLP9qkZDp/ljb9VsezHEvY/SKsCD+d6crMvXiiN7ib/FikKRW+gTdeT0DhDCPEvIcR/9Md9bX2uLyNMArPu54/pH0PSfskB2+x6fafndUtVz55BEJ9oZebvs7n05nHEJ2oKbdCw+IDtUtJtAev+fPR3VFU0887z27jw+B/aPFZlWRNSSt54eivrV1XsvfC9AKdBUYTKDFMoejKR3OY+gja4SALu1BWJFrtQAOkzM5AtLlprW2kubaa1ppUqQ8ppa3XPVhRGbntsf6orW8jIcgS8Z2wLkpphp7ykicZ6J9s21XDtOb9GtP/S4iZ+/KqYG+ZprdDz5GmdI3gPptXgelIWhaI3Ekkw+yTgFf315cA3aJPuFDqWOAvZJwxg4JmDiRsReCfe0y0KI3EJVrIHBlZ1AyQmey2KMy8ezpjJyQCUFkeezbSnsIH8HthYMZo4fVxPavysovcRiaJIAb7XXxcCbwHzoyZRLyfFb5AQwNYHN9Na3/vHjprMwtMaJHdUAkkpWl1JKEURrOK7pLDR048qGDu21vK3c39hx9bO6U67bVMNR4z4mA9e2d4p++sIxhjFvhijUfR+IlEURWguqiLgKbQBRqpHVAjs/RwMWTA0YP3uNwuCbN37+Nvdk/jPyzOIi7eQlKJZGIU76oNue8G1o7n+vkk+6/bsbsAU4tfjdLq4+KQfePu5fC4+6QeWfF/CupVaHGP3znoumLOIZT+WtEveu65dxfa8Wq48s/tmbRmznhZ9VqQ66ip6HZFc8G8CtqDFJBqBKuCvUZSp12NPtxM30tcFtfvtAiqWlFOzoZr6EBfW3oDdYfY0CEzUFYX/jG7AoyAcMb7FfQX5dQhDZ1spJbt31rNpTSUT499mw29ai5ANv1VxxqELOX7KFwA8cPNqvvmokNMOXhixrI/fuY6v3t8V8fbvv5wfFcvD6Hoq3tXAy4/mdfoxFIpoEkl67EsAQohkYLCUct+rmOoAA+bm0LC9ntJFpTToimHjP731CjPePyisC6Y3kJisKYy8db6K4oJrRzFkhDYNz78KfM/uBh/3yz8uXsYrjweOaTXS1Oikvi5y337RrnruvHpVwOjW2prQsSKXS3LVWb8AcNzpg4K2ae8o/tXYH7yyg7MvHdlp+1cook0k6bFDhBBL0OZmHyKE+E4IcVv0RevdCCGIHRLHoLMH03/ugID3m4p7v75N0IPb2zZqwekBQ2K57t5J7Pc7b2wiKdXG+KkpTJyeisUqkBIeMSjMt57b1uZxyvY0ktnfm4XV0Ea858YLlgad7+2WMxiNhil/+ZtrePLu9dR0Uraa069JpM2uPLeK3kUkv9jH0WZnC8CFVql9ejSF6mskjEpk5PWjfdatnL+UPV8GnxAnpWTHi/msvmJFj86YclsU7hYVSSk2ckcm+NyNm0yCS28ex19uGku/bG1SnjH43dzYtr++tLiRhnrvhXxrmAs+aAHsYOzYUsstF61m+eLA+g2jolhwwg/c/bffuGFeZCm/beFvUVhtSlEoeheR/GJ/BzxsWN4C5ERHnL6LMAsy/5Dls27rQ8F91dWrq9j9ZgF1W+qoXl3ZBdJ1jAS/brpxCeE9mYfN6VjrjuLdjT6t0KsrmkNvTOAdvJsb5y/lkzeKWDBnacB7RkXhVkSfdlICgsvpqyjM5t7tclTse0SiKEqB8frrfmjWxO6oSdSHSZ6SQlYEF0v3XG6AppKe66KKT7D6DC/KGhC8/sLNocdkkxNk8l5b/PX0xfz2a7lnOVyrkLI9jezaHjxZoNbgSpJS+lRJNzVEr76h1U9xVVf2XCtRoQhGJIrif2jKQQAvA0cBT0RTqL6Mxe+uO1h9RauhC2tzD+62ajILYuK85zP1kOCT8oyk9gus+G6LJj/3VF2YoPRZh38T0T4XnPgDs3I/orFB+67dz+AbQ9i6MTCjq704/VxPVW1YRApFT6NNRSGl/DdwLlqh3dvAuVLKe6ItWF/FkuDrrgkW1G4x3Pk27em5igKg3nB3n5XTtrXgnpS3N2zdUMPmdVVBYxGb17Z9YU9Os/L1B7sp3tXAyl80S8XoejJmZR09+lN+W1LmWW5qdHL5GYv57G1vL69QlJU0MnvcpwHT/arKlaJQ9C4iiqpJKZ+XUp6qP16ItlB9GUu8r0XRpAd2pUtS8OoONt6xjmaDu6nil3JcvaCat634hJu0fva2N2qDR25fx+/HfcZRoz5h9VKvSyrSu//WlsCWGkZF4T+5b+FHhZ7X7zy/jY9f28klpyxu8zhP3bORvHXVfPmeVsuRq9fWVFc2I9VMVEUvIqSiEEI4wzx6fz+KbsLk8P3KG3bVU/xZIb+c+CMFr+6g4pdyyn4o9dmm6OOeGxLKHKBlMgVr1xGMlLS9VxRGPn9HCzi/91I+R4/+NOg2I8cn+izXVnt/vmW6xRYuRhFvUIL+U/3CUbzbN1Zic5ixWAWtLdJHMSmiw7oVVZw8/UufmwlFxwhnUbjDlLuBdX6P9VGWq8/iX2RXv7WObY+GLzjb8Ww+pd+3r3VFV3HpP8Zx4tmDOXVeYNuSYCSlhp477s+BR7Q9mv2FBzfz1Qe7uPpPvwR9f9qh6Rx72qCQn7/mbK2vVLgLtzEOY7N7CwjbsgoqSn1dTGaz8BQgGgPr+Xk1XDL3x07rbxUJrz+1havP/rlPtxO5/NQV/LaknHOP+a67Ren1hFMUzwJ1QDqwGrhSSjnB/egS6foouRcPI/0wbdhP1crKkNulH+69UObdo/m5G4sbQ7qiKldUhKzNiBYZWQ5+f8pALNbIagOSUiO3KCZMTW1zm/q6VhacEHoORmycBWmYWW21BaamPnXvhrCKwtjx1TjWtK1BTRV+iQgms/C0NDFaNWccspDP3irgpgsD03ajxY0XLOW9F7fzw5fBR/U21Ldy4/wl/LrId+rxK4/n8ZdTfgzI5OqJVJVryrhSxYT2mpD/3VLK84Fs4GJgIPCZECJfCDG7q4Trq9hSbSRPTQGTb4YTgDnOe8fqyPS9qDYWNbJy/lJWXbIcZ5B21Rv+sZatD+XRtCfytt9djbvjbDjG7Z/CmRcPY+ioBM+6Y/5vAOdd2f62F3EJFoaNSSQl3caMWRm0NAdaAU5neFdQg6F9iHFO+OTEd/jkjR1IKVmzvNwncwqgeHeDz7IQGBSFdhFrbnZSoqdD7+xCi8JNbYjq8+cf3Mzr/9vKH2dqWWR566u55/pV3HzRMj5/u4Bfv+uZFq6RXt4hp0cRNgIppawTQmwFtgFT0ayLhHCf6WyEEEOBG4EkKeUpXXnsaGK2m0k7JJ2y77R4hGNADElTkkialMzWB/OQTolD9/+7KfpgF0itzqLglR0MPGswJv1O3ugGaa1pxd621yZiWiqbcbVI7Bl7H18wum4uvG40VeXNbPitkpU/a37kv9423jPnoqnRe4FOSrHhiPXtGxUJdocZu8PMv56ahskk+OXbwAtc4fZ6RvqNfTVi7BFV5VcDcdlpP/GAhMtP/4nDjsvm4KOyKN7dwDX/nhiQ3dRY7/Scg/sCvSvfG8fojhbkMsQhy/xuNk6a9oWPwuxqi0JKydrlFQwdnUhsXGSJE0IEJiYoOka4YPaNQojNwEJgOHApkC2lfDPSnQshnhFC7BFCrPFbP1sIsVEIkSeEuC7cPqSUW3Xrps+RdnA6MYNjEVZB//8bQPLkFIQQ5F40jKGXDPeMW3VTZMi+KXx3F1sf9lZ2uwx3xJ2dJbXs7F9Zcf4SXJ08dCc51cZhx/Vn5HjvRdo4Rc9YzxATZ8HhCK4o/O8cDzS47NxV0MGa/E05MA3QWn7cd8NvIeWsM1h9NUFqIN57Ses4+81Hhfzz8hU8edcG8tZXB0yza6h3EqPPTt++pZamJiflJd4LclFBA/V10c8TaW72/h3L9jRy999WBaQa+3f9bfBrytieoH5n8PWHuzlx6pf8+ahvI/6M6MTGjv5cP+9X/nzUtyHjVC89spmXH90cteN3NeFU8z/RRp5uRavOPh44Xg/GSinlCRHs/zm09h+elFohhBltvOpRQAGwRAjxAWAG/u33+fOklHtoJ62trZSXty/TobKyksbWWlotjTTRRXdLAvr9MRXplLhsTTSh+7QNo6kzz0in+NXSoB8v/WYPAy7KRLokDfneC059RRWWxrZvpRqb23Z1GP37tWUVWNsRjA7FX+8Yxq78BrKHSBoaq8ge4v2HdlFHQ2PgxdglGxEWryxzzsxi6JhY4hMtmEyCf17irVU4bUEmPy3UfjZO2URDY1XA/qb8LonTF2Sx6pcydm6rCyrnzD+k8d3HZVSU1Xp+TyUlvtumZtiwWAMV6K+LAtt/1NU206+/9i930/ylvPTIRs690jcJYNO6QgYNi6N4VyOJyVbyN9cxbEx8yEaC+Zvq2FPYyPSZaUHfB+237bNssHT+efkKAN5+fiufrpvpWS/xbhPsf6loVyXl5YHTHKPF289pF90VP5VF9L9dWVmJUU2093pg5PUnt7P4qzLufWmyp0/Xm09rzSw3rCkkc4C3NqiuppXzZ//K1g3a7+SoU1KwWLqmt5f/37m9pKaGjgm2ZcMJYJj+MBKRQSelXCSEGOK3ejqQJ6XcCiCEeA04QS/sOy6S/QYVVIj56JP3cnJ6TysqYRaIML1/YobFYMu20lwY+g5u5yMFVHxf6Vl2tqMld1u4DFXRbkulpbKF+k31JE5N7NBd25ARsQwZ4S3OyxnidbGFuiBmDrCTPcjBpAMSQcL0mckkJHuV1uARMWzf3EBaP5vP5ywWX/kOOz6J7z+tYvapmdgdJnJHx7FlXaCiECYYPDwWKOOT14u46t+jSUiyUlfle8cfl2jG7giU+R8XrQlY11Dn9HGfbVhVQ2WZr1Ksrmhl944GTpziDdAfdXImd/xvYpBvBU49UKvneH3xgeSOiuzCXVcT+PsoK27mo1d3c+SJmThizNhjwmd31Ri+B6dT8tV7RUw5MIV+/fe+oDIYHUon7iSD4r7rNwHw9fvFzJ6b7eMS9efTNws9SgK0pIXkVFvI7XsL4RRFbpSOOQAwlrUWADNCbSyESAPuAKYIIa7XFUoAUsongScBpk6dKsNpx1A4LDVYWs3YaX8/omiSM3cwO1/cTktFoLKIcSRR8f1qn3WmZhsxjtA+92D7CEVTtddSscpYYhzxrL9xCc0lTQy9fAT9jsiM+Dihjw9/unQ41RUtpKX7/t3+/sAU9hQ2MGKMVqdx8Q3JQfcx/5pxvPfSdv5w2kBiHN6/X3q/JJ/zO+GsQfzhdBcJ8drI2gNmZbFlXWB6stVqIiHRG457+NZ8/v3UNBrqtIvm6fOH8tqTWykuaOKjLYUBn3czYlyip1q8sd5FQqJv3On5//oOSnK12tmZ53sh+vKdYh54NYm3n93GzGP7039gLJ+8uYNPXvf+G23b0Mr+B4b/zbv/J4p3BnbPBbjtkrXs2tbK9fdOxmH3GvI2a2LAti1NZs/+Xn9qC3+fv4aMLAc/FUbiaGg/rS1eZRzp/7bmbtT+Xhcet5xH3zmIjKyY8B8KJ0OzldTUVPYUepMUYhwJpKZ6fydxcb6Wi4k4n/e7go5c+9oiXNbT9nCPTpcktBxlUsoFUsphoZREX8eaaGXA3OBWknQGyeJph0XRXNrMloc307CrIej7rYZ9ua0Ld+V4taGj695y8FFZHHvqwID1OblxPvMtQpGe5WDe1aPIHqgpiQXXj+awP2QzcXrgP43RFXDQUVlMOzRw/1abCbvBunn3+XwmxL3NxtWaG8sdV/GPQ/gTl2D1sSLcMQo3u/2mHVaWNwcNwD72r/X8fcEy5h74FVs3VnPZqT/x2dte91ZRQeDfr7y0iafu3RAQXzCm5vrjLmA03jUHazlSbbhpcWdAlRRFL9uuoQOxG+PI3RU/lfGfm1aH3jgSGfRW98amjv7Whb912Van495CdzTG34WWbusmR1+nCIO9n4OYIJ1XW4KkN1avDfTJg+ZCqF5X5ROULvmwlJIvill10TKfeIQbp+Ef1OlvcodxO0kpaSxu7LZWFVMOTOf0C4e1OanObBbMu3o0190zyae6XAh83C/gm5XUr39MgFsrGAlJFmbM0oLrYyYnh8zcitHX//hlMXdevTLg/YduXQtoo1Td42KN7Nxay5fv7/LJ0LropB+485pVzBy0kD+MX8R3n2mWT6iUWPAmBhhdPSt+CoyRGRsbdkXbdH9lFxF+YpXu5bAwj6IwnLu/ojD7xSOqgngBeiPdoSiWACOEELlCCBtaZ9oPukGOXsfAMwYx7IoRPusaCgJbaletqCT/qa3UbfENVlcuq2DddatZd7PXf95c6v0hNwaxKpyG7rYuPz9xuDz1gtd2sPKCpRR90HPbjxjJHZXABdd6h0s1N7kwh1AECUlWrDZTyAFEx//RWwmenGrjlHOHcO4VI7ngmlEhXR85uXEAvPtCfoCV4U9jkI7Dbz+Xz0Un/sCNFyzxrFtmaAVTUtjEzQu0gr5IFIXxAnjFH38O2M54sezMsbGhaGuqYTD8uyCEiy1EJIN+01Tloyi8Nw+7d9Rxzdm+HQLasijWLCvnhguW9PiOwpElJHcQIcSrwCwgXQhRAPxDSvm0EOIS4HO0TKdnpJRroylHX0GYBeZYM7FD46jfqgXMKpcG9zcXfbCboo92s9/T07Cl2Sn9oYS8u7XMoNr1NTgbnCB8C/6a9jQRM9DXajG6ntpjUex6VfOf73xxO9knBI6C7ekMzI0jKTl4EDI5TVvviDH7TN5zM8KQ7jtiXBI2u5kDDtOsilETvO+de8VIdu+oJznVissVWedbgMKdoRXJx6/v5IHXoClIKvOeQs3CC6co3AZgqN5XNruJ5iYXvy4q4ZZLllGQX0e8oSOyyyWjojiCfc9t4X8js7eKoj6oovDu82/nBk5EbEsBnDj1S0CT9Y4np+2VfNEkqhaFlPIMKWW2lNIqpcyRUj6tr/9ESjlSjzvcEU0Z+hpCCAb+cRCZx2rT8grf1bx2lgQLydO0am+rOxvIBXW6QnErCTdLTvuJ2nW1tJR5LxoVfs3Tij8vYsv9mzzLW/67OWhFeDiCubN6MmdePIyxU5K56MaxJKbYuPKO8QHbuO8Sjz1tIENGBmYapRsq6oeO9g0EW20m/nDaQLIHxjJxWion/3kIh88ZgKsd39P9fw/MqDKydkUF2/MCU59bml1892khf1+wDIAjT+jPoGFxPttUljWzZlk5+UE+D3DdvZMAzeJ66ZE8vv240Gc++cuP5XHe7O944JY1QZVVR2nsBEXRkcwpo+vU3VI/VIwif3PgdxappbA5hLu4pxBVi6KrEULMAeYMHz68u0WJOv53/s5GJ5nHZNHv6EyEEBR/WkjlskoKXtlOvKEVhpGST8torfBaFMUfF1K+uJTRN4/D0T+GbY8EjmoteNV7UYiksC9YsL0nc+jsbA6d7Z1CaCwGdDP1kAyfbb/7tJCmBic7ttYydFQiqRkO5p6fS3KaLah76vgzB3P8mYN91vUfFDrTLibO3C4f/Qn7fcE9L3gTCYeMjKWksIm6Gifz/vC9Z318opVLbh7HtX/23gnXVLV47nKDkZAYvo7m1kuWA7Do8yLGTErm6JP2PlW9rKTRp8Dv31evZN2KCp75bCbWID3GpJT89msljQ2+v89Iphgu+7GED17ZwXX3TCIm1uIz79wtQ3UI15MIctvtn/4cio5YTF1Jn5ryLqX8UEo5Pykp8tTQ3oot3dctkqI30HP7ZW3p2l1t3ZY6Nv3bt9lvmj6JrvqXaqTf9LWWihZWX7GS8p/LCEbtRm8FrzOSO7Ru1hPNFc1svGMd1Ws6dscmhGDo6AQsVsE9z0/n/KtGMccQgwCY+ftsjj45h3lXj+bwOf0BOPKEAUw9OCPi44zfP5WLbhjD1f8O7LcZTomEwu0rnzgthb/ePoz4xMB7wvhEa8i6lVDEtaEojJQWN/LZ2zs5YsTH5K3v+KTAjw0WC8DT923kp4V7WLMseBHdJ2/sZN7vlwRMFmyOwMI57eCFvPxonmfYlPEz7uyvckOzxyZDfy9TkKBdpA0JO2Ixwd670yKlTymKfQljoM4cb/F0o3XjyPIWPtX4+b5jcmKwZ3ndI7FBsqmMLicjjYXeFEhnuABjkF+WlJLWNjqudjY7X8in4pdy1t3QdmpkU0kTTUFGz17xz/Hc8/wMElNsTJ+ZEZDi2llMPiCNnCFxAetHjE0ybNO+HPn4JO2GIjY+MNsqLsGCLURAPhTtyXCqqmjmklMWsz2vlpvmL2n7AyEINskQQk8KdKf4+tPWXfttly33vC7Qq/WN6c/VekPIcsPUSaNFYQqS0FYf4e+9I26xXxftYVzMWzz273Xt/mx7UYqiFzPgjIFYU20M/OPAgAppR04MWcdnY4oJ/BObHGb6HZ2FsAgcQ+wMOHUgaUFqCQBSZqQSb6j4bTH8c/pnQRkJVm1e8OoOlp71MyXfeYu5KpaXs+3JLVFzUbVURpaeKJ2SFecvYcV5S1i5YCmthrRgm91MbHzXeGmN6bM2u4n9fpfGsacNZPzUFMZPTWHw8NDFWyedPThgXUKSZgEEUxTxiVafdM7EEJ19hYChoxM48y/+DRrCY3S7lJc20dzsZM3y8rAp0/V1rdRUt7DbELAPlWJatKuBJ+5ez5mHf+MTDwmVjVde0uSTPuzG6XRxzNhPeeGhwN5MRkVRo/+WjA0T3Xf0a5aVs2OLtyJ78gFaS5X62sh+f/6dh4Px3kv5HD3mE3Zuq+XRf63zdPa9L4KboL2lT8Uo9jXih8UTf3Hwtg1CCJImJpMwNpG6vFqQsPttLfBtjjFjS7Ux8Kr+mKwmTJhIGJtI2SItndKaavMohOT9Uyj/MXivKWdD6BiFMAtki+8FoeSrYnDBlvs2kX5oBkIINt6i3Q3FD48n4/C9r/IOkMPgwy75ppj4A4O3mGgx+MAbdzdS8nUx2ce3P1ur6rdKWqtbSGuH28mI0VK84NrRTJymWRCX3jwOgO8+DawCt1oFZ182kqmHpPPuC761sIl6YkOwUbX+7qiBQ+NZu8w3i27eNaMYMymZ+DZcTsNGJ7Blg++dvzGQW1fTyl3XrOL5Bzdz3T2TmHe1loq8aU0lA4fGExNroaqimdljP/UU7n21+Vg+eHk7H7wcvL53z64GHtTrS775aDez/08rz/JPi3XjbJUs/qo4IG6Sv7mWLX6uMbcyM7qeqqtaqK5s5udvvDc6bkXhH9dxK+hIY0uRxE/cw7mOHv1pl3caVhZFH8dkMZEwOpG4EV6FYtYLyUyGi6g7U0qYBVnHataGOc6MNdlKyoFpASNcQfP/B6Po490+PaK2P7uNZWf/QrNh4lurX3VwpHf+7cVk9V40ttwfuptns5/LydXSMQtn/U1r2Hz3RloqO54XP0T/Ww0NkoQQzLKx2s1Mn5kRNC3VfYH399eDVjVuJDsnsMYjvZ+jTSUBcNr8QGvD6J8v3tXA8w9q3/+d16wC4KeFxRw74XPm/WERAE/evd6nuvvIEZ/w4C2hM+eNVofxzj+cxfLrIq2KvHh3A8sXazdAwdKF3QOqjPutr23ljEMX+lTONzW6gh4vXlcUdRFWlDc2OHnh4U0sD1Lc6E93tKNXFsU+gsliIn5UPK5mV9CLvsliYtjlw/VaDQtDL9Uyx4RJYE+3M/yqkeTds8kn08nV4KToo91kHdffZ1/5T2z1WXan8BppKm7EmmS4AEWpZktEOHnPX1HQAVeY0X3mbHTR0T671949ieYmZ9BYSFwQRRHOv+22Gg44IoW8dfUcPiebd5/fHnRf/q3FASxBJgKmpNsCxrzGBKk4/+q90A0XmpudnkK+X74tQUrpk2YbCW89s83z+sWHN1Nb1cIfLxoe0qIA79TBg3M+QEr4ZPUxPPOfjQHbvffids76y4iA9vPuFi5uGhtaKQzSPsX9XbbVesRsFjidEpcLbrtU6+SbJ08L+5lgPHP/Rg44Ip60fp07k95Nn7IohBBzhBBPVlX17Jzk7mLA3IEMPHNwyH8kS4IVs35xssRZsBgGxAghSBiv1QQkjEkgbZYW0yh4bYe3q2x1S8SzMBqLI+8LVLmygrU3rKZqVSUuv4E5ziYnWx7eHDqrye+C72oJLp9/ELsj9R/GuIYMcZxIMJtFyIC5vxUAkGaY4WHsqAuQka1ZCSPGxXP3c9OZaUj99R9fa7WbOXR2lu+6IIr2qjsmcOypOZw+39si3RFrJjdEGnYwZg7+iFLDb+Dmi5b5DHEKRqhKeNB6Od188TKOGv1JWIVTWdZEVYW3n9aVZ/7Mx6/vDLrtKQd85WlXH4rGBicbgvQ8c7d3aSuA7t8mBqCmqpkn7lrPD+0Ya/yvK1dy/KQfuPf6DRF/pj30KUWxL6XHdgf9jswk67hsso7vT9pB6djSbLRWt1K9torGokZWnL+ETXetb3tHaFP6jAghkC5J3gOb2PWW9o9bu6mG+u11bLh5LTVrqlj/9zVs9Qs47np9JyVfFIfMavKvJncGabEN3kaHbvyD6631rW32rTJWuTvbaBbYUYxWwPlXjWLyAalcdOMYz7qr/zXB54Ka2d/XnRQTZ+Gqf03gxvsnB+w7OyeGP140jGmHeBMbgl2cM7JjOOGsIaSkexVUTKyF+deOYubvs7j81nFtnod/A8FXnwjs4OuP2+8/eHg84/ZLCbrNto3BM6TcVJa38ONX3jnhwfpmtYfGBifb8wKPGf9lMUcBjXWtVJQ1+XScNRLsnu3YiZ9zz3W/8XfDDPX8zeHPy827z0WnbZ5yPSkixmQzkaSPKQWIGxFPc1k5VSsrtbv9JheVS4K3FPGnfnudz929s9HJ+lvWUr2yEoCMIzJZc/WqgM+VflOCyWYi54+DsaXYqNkQPj/fX1G01rQGHb7U5FcYZfxcfX4dv122gowj+jHs8tBzu1sNGTWdPQ3QjTFGkZMbx/SZvkHzrJxY7n/lAD5/p8DTNsQf/yLC6+6ZRN76KiZOT0UI4TNl0BEmFdjYC8tiFaRmOPjjRcODTr8zW0TQOEl7SEiyUl7SxMChcRx/5mCfQsFwDB2dwNDRCXz13m6qK5p59Pa97xgUn2ihtrqVpnpnQEX2MScNgHd3kQP8VN/KzCEfUV/bypr6/+M/f1/NlnXVPPnhIZjNpqDdhwv1Xl87t9UhpUQIwZEjP9lrmfeGPmVRKLqWOL2RXdXKSp9CvLDov7jq1VU4DWZ5wWs7PEoCoDSMyb/n82KPZWEMkEunZOfL29l4+zqayzQLweWXmRXMoih4fQflP/gGEZ2G/PdiPdOo5OvwbgijReFqh0XhanJSsaQ8IuViTJ8N1bTQajNx3OmDSM+MbIhQ7qgEjjoxx+OSPHR2NjNmZTDvmlFBYyJujAaW0Z0ZLMPqzIu93RKGjw2cb9EWFiBOP99BQ+NJSrF5ihvb4m93T+JIPYNte15NSCvC2IerLU67QHO7NTY6A6yYE07zNsc2Nbg8tRQ3XbiUZ+7bxHefFpG3rhoppScwHRMXvKtwTVVLu4vqKsr2rktuMJRFoegwMYNiwQT1IUaJBiNxbCLV66ppqWhh/S2GnkV+19XiIGmgRqr1IsJmQ2ZN9Zoqdun+ZlOMmRFXjfJYBpZEC63VrWy5bRs5F+Uw4JhEWms0t1nBy4E+bZ/CwHBtckN8pj0WRf7T29jzWREZR2cy7JIRtFS30FLZQmyQimxjZlOw4HNnkJ7l4LwrR7W5XSglYjIJxk9NYY2hYaWxUG/itBTy1rWvUvtUwL6hhuIUK+Onam6nU87T2qS881x+yM+lpLsLDjVZXWH097j9Uzj3ipG4XJIb5i0NvSHebLLP3vIW983/22gGDYv3yZiLBdxn+t6L3jTfbZtqPIrcbBHc+sj+LPm+hHdfyNdiR1Jza+0pbP+Mj/zNNaSkdW5QWykKRYcxWU04+sfQGCTrAyDjyH6UfOW9CzfZTKTP6kdrvZP6rXXUbwmtYJr2hL8rcjU4kVIiDcHz9YZmeeU/luK6bAQNuhlvTbZ6UnILHiugdkldyM674DuHI9KMrNZqo+spcoui5EvNZ17yRTHDLhnBygXLcNa2MvmJ/XFkB6asXnPnBKorW0hK6Z4Rm253yNDRCfx+bg6DhgXW8sy/djRXnfWL547Z7e7KHBDDAYdn8skbBQwcGkdNVYvP0KVhYxLYsj7QOnVf9m7653hi++kXWLPgmJNz+N0Rmbzw4GZ+W1JOLtqF2d2Axj3IKtJWJSnpdlwRZLz5W0Vms2DyjFTMFhNNhnhXqOYrl5yy2JORZrEIklJtHHnCAKYdmoHLKXnyrg1s3VhDSVFDxNXdAPGJZp+hUp2Fcj0p9oqMWcELy3LOHESqXp0KkDwthaGXDceabCXnjIGYQwzwaQ/OMP9AslWy/Wlv+qTVr2V4OCUB0FrfdqUvaI0R3UHuFkOMoj3BbP90Zfd5rbxwWcBMEYBhYxIZkWYP30IlmJztyORy1rey+e4NVPj1U8p/aisr5mmV60IITvzTkKATCO0Os8/EwtQMO3c+O42b/juZpBQb/3p6GpffOj6gLmTKgek8+MaBPusyDH3N/BVw0ce72f3QZmJiTKSizTQ4Xn9PAL/TR/X6Z/oFc4+5YyimEG1KUgxy2Oxmn8mILpf0VLkbY2/hBq+6Jw0as8+SUmykpNtJ1G8CXno4j1W/+P4NzGZBelagWzEuwcztT41l5u+zA97bW5SiUOwVsUPifC508SPjic2NJXawdi/V79gsbGk20n6XhtmhKQchBI4Bwf+F4kf7plj2OyaTnDMCR6QCNOwOb5YXf+J1X1mT21fVYLQOjL54Y+ZTS1ULy87+hS3/0fpiGQc/uUL4lRt2N9Cw29cCM9lDK82C13ZQuqiE+nyv9VW7sYa116xi+XlLfFJyQ+FqcrH+Lxv55cQf2XDb2ogaJO5+bxdlP5Sy8VZvHyEpJUUf7Ka5pInynwKbRrqaXVQs9cZaxk5JBrzV4Slpdmz6ucbFW7DaTAHFfDGxZuwO7/dhMguuucWbReXfKyz/ia1U/FqOZWc9RrtmP+AcwPx8PnVbNWWboV9c0zLtAY0d3efnZubvs3xGqQIe2S1A2eJSzv7LCJL0tifG+IYxNTqSdo51NYF/wxR95snn7xTw6B2+vZweffcgbvrvZM+y3WEic0AMF/wtsIVLZ6EUhWKvMRlSKAecqtdq6L70lP1SyL1oGBa//P/EIIHD2KFxZB3rm8efvF8K5rjgHtLCd4M3f7MFKTryKe7zI/XAtIB1zeXNVK+pwlnf6ltkqL92tbhYd+NqnPVOSr8roWJZOeU/ei+ewVxPziYnqxYsY9WCZT7ptyaDW8S/TqR+Rz15927kt8u0YixnfSs7ns3XXzsp+77tSt6GbQ20VmkXo8qlFeT/TyuIrNtay9ZHNvtka3nO3y9duPjTQpae+UvAdkZ2PL+NjbetY5ue6jpkRALX3zeJfzy8X8jPjDVk0YGWvguQgHZBHjoqAYvhu/Kv6HczaVqaT6PiSYbzWP3XlQBcc9dE/vHwFP71v2k+8ZUT/zSYtH52DjnG+9v740XDeeSdgzxW0cTpqZ552NOBzXduoODZbdx4/xRmHZvFuVd4s+GMv5f29/3VOOSYLJ+5Jv44YswkJVpISrHywOsHcttj+5M5IDrFdtDHFIUquOseYvWOp+25a08YE1iclXFEP0x+AVphFj4XUiPliwPvaodcOBSbX3O7YZcPD6lsABInBsl2ccG6G1az6d/rfVw8ztpWij7ezfp/rPHEPwCfO2/QlELD7gZq9B5CrhYXKwwB0lWXLPe0aTcbzs9ZH5jO60ZKyYbb1nn2CVC5ou10ZP+La6M+6W71FSvZ83kxOwxBVs+x/Pz02x7bEtbVB1o2GuATlxoyIoH4RCtNJY24WlwBhY37H+zrtrLaTDTsbuAU4GSgpcXl8x201hotPa+MOUPjOMdvTLARV5OTpBQb/Qdpv9U0Q0bY7+cO5F9PTSPB72bGZBIcd/pALrl5LOdfNcpTvOjO39rzWRFJqTbOWDCcZEPw2Oh6Svb77QZryzIJPLVDbgYMieOO/01jpN8Nldv9VfjeLk6ubuW6K0aGrUTvLPqUolAFd91Dv6MySTkgNaSLKBhCCAadO8RnnT3djhBajykjZkdk8Qx7tgN7hj2gT5MlwerpbxX0cxl2hl4yzKcflpuqVVW01nov3uW/lJP/xFZq1oTP2tn9ZgGrFixj7d9+o6m0iZq11bQaGw/uamDJaT9RvaYKYejg2urXd8h4cW6pbKHGL1uoNsycB1eri6pVlTQV+9aIuBqdWssS/Wtq3B2YjNBWN193sL9yRQUr5i+lZmNNSIVeuaKCFecvZekZP7PivCUUf+51CSYm27j7uek+29forrE4tL5GRneTTwqy4c5dtkpiwrjwmv0CvMNGJ3LuFSM9LpzyxaUsOe0nKv2aIpotJiZMTcURY+a0eUOZNi4R41GCFWHKZu+67GQrD7/9O8/yuP1T+NMlwz3psDY0N9nOFwKVNcDUgzRFOgGYNzqBG+7T5HVblQXP5bPx9nUUfhjd2fR9SlEougdzjJl+R2Zia2dKXsyAGIZfqd0FWlNtntbk/U8aAAKSdB+38QIUEAQ3/IJjB2p3fBmH98NkN2GOM5N9cv/gn9PJ/j8tv96abCPr2CziRycEWEbVhhYNlcvD38EHc6mt//saNt4efGbApjvX+6TS1m8P3cbCGKdw01Ld4nOxaixq9FgqefdvYv3f11D4YmAriKKPvBdrEaQew6hsgwXB3RfvDf9YS1NRI2uvWeVzETey6w3tbtl9Yd/2yBYfmZNSvUHi+AQL0uB9m3ters9+WwyK1KhEnQ3OsJlmwQL/BxzWj4FDtZuDTXduwNXsCttZICnVxni/2S7G1HBnk5OSb/b4NMtsqWzxtEHZD7BsqeXgo7P476sHcvTJA3ziKv5uR4Cph6QTn2hhKuDcUIPwq2iv31pHxa/lbP/f1oDPdiYqPVbRrZhjLQy7YoSPMogdEsewy0d4rABhEgz682AwCRzZDpwNTgrf303KtBTihsdTn19P1apK0vUqZUeWgxHX+NYBBLMoYnNjSRzjTXO0JFgZcEoOFUvL2fNZccD2AJW/Bp+qBppCc/R3UO3fOG5X8PRh0NxCRtfQni+KdFksARfe0m+DFPy5YNXFy5n44BRaKptZuWApjiwHkx7bn/Iw8Qtjo0a3gnZfvIUQPnELZ70Tc5wZp6FldkuQjquhsARx+zWXNmHP8Lp/Lrl5LAX5deSOSmCP4eI7dkoKu972xqJ83FCGQL6r0embdeCHs95J3dZaYgfHBZ2V4t2R9iSlpPynMuKGxeMIU7hY9mMpcbqyKXhpO4Xv+97Zu5pcOOtbGRBrZlK9EwwZTMecnEPRdyWgdwVwNbkwWXzv3eMSrNz+5FRWna41UAzVqyzaKItC0e1Y4iwB/yCWeIvPP3TMwFhiBsQgTAJLnIWBfxxE/IgEhBDE5cbR/8QBYbOHgrlFWoK0mgBInpKCPTuyqmYj5jhLxE0RQ1G1ohLQLCx/Sr8pCfqZxl0N5N23kdrNteDS5mkEC3InTU4ia05g6qQ7LrL+5jWsvnwlxV8U+UxFbK1tDTivqmUVVK2uDHkePtlhQZSK8U68qaSJmB9KmXVAmtbzy+967++y88htcAk6G5w+re392fX6Tlb/dSU7XswPuY2RymUVbL5zAyvnhy+8q8vzpi9XhEi5bi5v5vy/DA9YH59o5eQTvTNPQv12fBpEdtNoYaUoFPsEQghiBvqm5PY7MvigJGEWDD53COmH+/ZKitErpS2GDKp0Q68lS7yFpMnJxAyKIfuk/kHdUMEyrIJhiTNjbsdUvfLFZWx7JM+zXPqNr/WRfGgiWcf1J2FsIha/gUWt1S24Wl1Ur6qiPr+ObQ/n+bxfu7kmYAhV054m1t+4hlC4Gpzs+aqYLQ9soiFIQabRhbflwU2ULtzD+pv1/RlcXVJKn9kedVtqaanWYjXGDsTOeifOMNXwVbr7sPCdwKZ5RqXmflW/tc6zYsdL+fx22YqAvmHga9WYg7TvB83i23mPt5W5Mf7TaHAlGV1nu97YSdnPpQHbh6uF6UjH40hRrifFPsPAswfT5KpFOiUOa0LYbBFhElgNF9RB5w3Bnm6nenUVccPi2PqwlgIaNzyO0u+0O31rihVLnIVBZw8BtIuXvxsqfVYGjv4OYgfHUvjBburyvHfW2ScN8LiEtDkgtoCqXEuSxZPq6o/RLVO91ve4pjjvsKrci4chWyW739lF/dY6WvzcX/7k3RM4r6Et6nfUs/XB0IOiij8pIuu4/sTkxFKvtxdvLm2mqbSJ/Ce9/nbZ7PIdaiWh8P1d7H7TNzW6MMzsi3BsuG2tTxsY2eyifke9T/bd7je0Y1X8Euh2NLrjwlm0Rkq+LsZkNxEzMNZnDoo7VtVQUM/Ol7Tg9q7cOEbfPNawTWirydXo0vKKo4CyKBT7DEIILd3WZooopdBsMPkdWQ5MNhPJ+6dgTbaRdkg66YdnYO/nQOh1JMZKdPCmDfvIYBYkjEnEHGshzS81NHFcIhlH90NYBHEj4oPWgww+Lzeic/V3w5gNs9NNFhNmh5kBp2ojQVtrWmjxm1aYMiM1oPgR8CQftEXpt75uMqMVZsvQ3Gpuq0IYrkLbHvO3Zmo9LsK4Ydr3ueezyOc0hMPV4qJyaYXXetD57ZLl1G8NrIh3BhkQ5c7+aq5oDshI87dg3Wx9OI+8+zax+q8rg2ZxGZV2/bY6tht6WdXn19FUErzQtPjZQvJu3sLOH4O7KPcGZVEoFCGwGuoxhN+YUaPLadDZg5FOiT3D98Juz7CTe9FQyn4so/q3Kux+BVQxObH0m53pEzhPnZ5GytRUhEkgDW250w/LIC43LmhgOBJMscGnGgqrNtt89RUrfd5zDIgJehtpDtN23IixKj5xQiIZR2RSv60OW5qNxsJGij8touz7UrKPH+ATi/JvU7/uhtUeV1n86ETqttSFzK6KCAHlP5ex44V8Mv2GNBmpCtJhttnQlTXz2CyKPynyxHfWXvdbwPbWFCsNO0MnMoBv/MZtLRhrRcA3BbrglR0UvBJ8MFP9ujpc9a4Oj/ENR59SFEKIOcCc4cMDA0cKRXuxpdjI+ePAgKpyfxxB+u549pFmp9/Rmdgz7CSMC2yvnTAqgT2fFfukqLqVkrGuw5HlwKEPIeo3O5OWqhaqllcGuiIEQQOe5iCKArRssNYW3wtv/OgE4ofH4/K7g845U6uTSZyURPWqyIpa40clkH2CFrD1xGz0U63dWEPJ18UBStgf9x12/PA4gueitQMJm/6lpcDuCjHZDgIr08E7bMvWz07SlGSKPy3C1ezSCgmDdHm1xLddgOrrenIrCu18bRk2mkuafZoMhsNVr30+LszvsaP0KUUhpfwQ+HDq1KkXdLcsir6BO/VxbzA7zCGD2JYEK7kXDcUUpKjQEmshcVISdZtrPUoCIGVqKgAt5S2eOSBpB6cTOzQOa6KV+u11FH3o26Y9VDGcOc7i4+pImpJM1h+0zCijuyjlgFTicrXvIuvYbFprWgNcNsGIHx34/dmzHJ6277veKoioXxVo35XbAgJNkToy7ZT9EFihHwx7pp2mYu9FN8AyMQsSxiSELKZ0B57NDs11abKbcDW6fDsNGwj1nRsxugjdMQq3XLY0O80lze3OdIpXikKh6HuEK1TMOk67aAeLqWT+IQt7tp3kSck+Vk9ScjLSKanbVoc93U5zYwOWpBCzI6y++zW6u4z9sRIM9SbCLIgbGudRFI7+Dhr1Bo05Zw7yzPeIHRJL0oTkgGMKIRgwN4ftT+eHrTEJhiXBQku55ppJPSCVmIGxVCyt8LngDjg1hxbRyJ7XfVOEY3PjfBSFPwljEkg7KD2konArZXdvM5PdjKvRFdCo0E2wmEY4WqpbqFhS7ulCbAuSIt0mZrCH6WvWUZSiUCh6MOGC7pZYC+kHB2/znrxfCsn6XOkmAgOzboxV0LZ+dlIP8lo+tlQb1lQblgQLjv6+d6lJk5JpLGwkZWoKZYbpgHG5cWT+IYuyH0rJPDZ0u2tbRqByTJ6WQlNRY1C//sA/aZ1RLfFWj6KwJFgRJsHQi4dRm1dH0QdasZt0SoQ98HuLHRxLxc+hCyatSVbt4mzCU3iXfUJ/EFD4nreQzq3YzQ4TrVWw58vgDrGW8uag6wHsWXaainyV1rZHfeeGW+ItmBymsPUhRgsLwBxnjkrvJ6UoFIp9GUMNQe78oT5vCbMg9yJtnf/Fxxxj1lqtABa/VuHJU1JInpIS9rAmi4nU36V5Gjs6BsSQqXdv3Xh7YBsNd9t6Y1GaO9nAHGshaWISmLS7/rgR8dTs9gbFM47qR9zQeK3d/cHpCLug4pcKTwsQxwAHIEg9IA1hFthSbZ4Ru8JmwmSMH5kF6YdpytntLjRWuYPmvqtaUUnK9FRqNwUq6ewT+lO2uO2uvyaHCXOsBVdjcIWTNjOdtIPSKf6siKrlldrKKNVSKEWhUOzDtHX3GcndafqsDJpKmkiZkdquY2cc3o/0WRlaPySDu2TI/Fzyn/QOnUqe5lU6lkQLTUVa7y5/2ZLGJ5E03jdgDpA6w2slpeuDtuo21dKgK4r0QzOIM0zps2c5PIrCZDNhMRQ+JoxL9HQRyDiiHzueyQ84r8xjs0g/LAOLIUPMmmxl6CXDcTW7MNlMntqbcJjsZiyxZlpCGEExObEIk5Zu7VYUlpToXNJVHYVCsQ/T75hMTDHmoK09IsUSZ2HwOUN8+mZFijBpd/DGFFl7P6+bS1gE/Y72VtBnzs4iYXxiQOfh9mJs/Gjxm3YXM9A7RcJkM2FLtxE7VKvhiDdkosX0jwlIeQZNubqVhHuol9vV5o5vuAyxoGDZcKClVwerpck8Louk/ZI9VlZcbhzDrxpJ7KRYUo8Kb8l1FGVRKBT7MI5srYNvV8w0aA8JYxKoWV9D2iHpPrJZE630N/RHCoVo4xbYapg37p/+nDAqgT2fakV9JqtACEHOaQNpLGoMiNVYEqyeAPmg84ZgsvoeeNCfh1D+UxkZh/nGkhJGxVO5rJK4YXFBpyFmndAfW6qN2CFxXreSTvLkFJjsu705xkzCoYnYk6IzR10pCoViH6enKQmArOP7kzy1wefuvj3Ysm3Yc2zEZAVWx4PXohBmETCz3BJvIWVGCo1FTdjS7Z7tYoKM7804PANXi4t+R/bDkR34vj3DTvbx/QM/d0Qmjv4xxI9MoGxxKXVb6rDEWzwZVPEjNcvFbTW4GXxBZJX5nY1SFAqFosdhspqIHRz8Ih8JwiTIPicTO8HrYNwxkVBZQv2OCl21bcTez8GgP7V/VrXJZiJpUjKg1cCYYywkjkukuawZ6ZKY3fO54yxknZBN6belZM/JDtvyPJooRaFQKPY5HP1jcAxw+ASxuwuz3Uza77SAe7DZ7kkTkoPWo3QlSlEoFIp9DpPVxOBzu8eN0xvpU1lPQog5Qognq6oi60OjUCgUirbpU4pCSvmhlHJ+UlLgwBiFQqFQdIw+pSgUCoVC0fkoRaFQKBSKsChFoVAoFIqwKEWhUCgUirAoRaFQKBSKsChFoVAoFIqwCCmj07+8OxFClADbO/DRdKDtRvF9C3XO+wbqnPcN9uacS6WUs4O90ScVRUcRQiyVUk7tbjm6EnXO+wbqnPcNonXOyvWkUCgUirAoRaFQKBSKsChF4cuT3S1AN6DOed9AnfO+QVTOWcUoFAqFQhEWZVEoFAqFIixKUSgUCoUiLEpR6AghZgshNgoh8oQQ13W3PJ2FEOIZIcQeIcQaw7pUIcSXQojN+nOKvl4IIR7Uv4PfhBD7dZ/kHUcIMVAI8Y0QYp0QYq0Q4nJ9fZ89byGEQwjxqxBilX7Ot+rrc4UQv+jn9roQwqavt+vLefr7Q7r1BDqIEMIshFghhPhIX+7T5wsghMgXQqwWQqwUQizV10X1t60UBdqPDXgE+D0wFjhDCDG2e6XqNJ4D/ItorgO+llKOAL7Wl0E7/xH6Yz7wWBfJ2Nm0AldJKccCBwB/0f+effm8m4DDpZSTgMnAbCHEAcBdwP1SyuFABXC+vv35QIW+/n59u97I5cB6w3JfP183h0kpJxtqJqL725ZS7vMP4EDgc8Py9cD13S1XJ57fEGCNYXkjkK2/zgY26q+fAM4Itl1vfgDvA0ftK+cNxALLgRloVboWfb3ndw58Dhyov7bo24nulr2d55mjXxQPBz4CRF8+X8N55wPpfuui+ttWFoXGAGCnYblAX9dXyZRSFuqvi4BM/XWf+x50F8MU4Bf6+HnrbpiVwB7gS2ALUCmlbNU3MZ6X55z196uAtC4VeO/5L3At4NKX0+jb5+tGAl8IIZYJIebr66L627Z0VFJF30BKKYUQfTJHWggRD7wN/FVKWS2E8LzXF89bSukEJgshkoF3gdHdK1H0EEIcB+yRUi4TQszqZnG6moOllLuEEP2AL4UQG4xvRuO3rSwKjV3AQMNyjr6ur1IshMgG0J/36Ov7zPcghLCiKYmXpZTv6Kv7/HkDSCkrgW/QXC/JQgj3DaHxvDznrL+fBJR1raR7xUHA8UKIfOA1NPfTA/Td8/UgpdylP+9BuyGYTpR/20pRaCwBRugZEzbgdOCDbpYpmnwA/Fl//Wc0H757/dl6psQBQJXBnO01CM10eBpYL6X8j+GtPnveQogM3ZJACBGDFpNZj6YwTtE38z9n93dxCrBQ6k7s3oCU8nopZY6Ucgja/+tCKeWZ9NHzdSOEiBNCJLhfA0cDa4j2b7u7AzM95QEcC2xC8+ve2N3ydOJ5vQoUAi1o/snz0XyzXwObga+AVH1bgZb9tQVYDUztbvk7eM4Ho/lxfwNW6o9j+/J5AxOBFfo5rwFu1tcPBX4F8oA3Abu+3qEv5+nvD+3uc9iLc58FfLQvnK9+fqv0x1r3tSrav23VwkOhUCgUYVGuJ4VCoVCERSkKhUKhUIRFKQqFQqFQhEUpCoVCoVCERSkKhUKhUIRFKQqFog2EEEOEENLvURmF49yi7/uUtrdWKLoO1cJDoYicFcDd+uvm7hREoehKlEWhUEROCVox01fA10KIc3QL4CV9NkCpEOJq98ZCiAv0+QB1+qyIg/X1NiHEv4UQ24UQDUKIRX7HOUwIsUEIUSKEmKt/5iB9nkCjvv7VrjpphUIpCoUico5GUxYleFskAByG1ue/CLhHCDFJCHE42qD7EuBKYBDwgRAiDW1WwHVolbWXoLUEN3KEvr8k4E593bVoVbl/AW5Da5OtUHQJyvWkUETOL8BN+usKYIL++hkp5RNCiFbgKWAmmmIA+IeU8kshxCDgBrRBSnPQWoycJqWsCXKc/0gpnxRCXIQ2cAa01gzHobUiWY7WlkGh6BKURaFQRE6plPIr/bHMsF74PRuRfs+RUK4/t+L9H/0bcDKawjgfWOpuAqhQRBtlUSgUkdNfCHG6YdmqP58rhNgBXKYvf4fWpO0q4FYhxDD0UZzAz8CHwFTgdSHEW8BEKeVf2zj29WjjTteiDaLJBRKByr08J4WiTZSiUCgiZwpaN143V+jPXwMXA1nANVLKVQD69LFrgf8A64ArpJRlQog7gRjgTLQ5Cr9GcGwXcKl+jDK07rA79vqMFIoIUN1jFYoOIoQ4B3gWTTnc283iKBRRQ8UoFAqFQhEWZVEoFAqFIizKolAoFApFWJSiUCgUCkVYlKJQKBQKRViUolAoFApFWJSiUCgUCkVY/h+prtugwprzawAAAABJRU5ErkJggg==",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Train MSE: Prediction: 0.0744 - Reconstruction: 0.0612\n",
      "Test MSE: Prediction: 0.0897 - Reconstruction: 0.0714\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "from tqdm import tqdm\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "import torch\n",
    "from torch import nn\n",
    "from torch_geometric_temporal.nn.attention import MTGNN\n",
    "from torch_geometric_temporal.dataset import ChickenpoxDatasetLoader\n",
    "from torch_geometric_temporal.signal import temporal_signal_split\n",
    "\n",
    "from torch_geometric.data import DataLoader\n",
    "\n",
    "loader = ChickenpoxDatasetLoader()\n",
    "\n",
    "lags = 10\n",
    "stride = 1\n",
    "epochs = 500\n",
    "batch_size = 32\n",
    "\n",
    "dataset = loader.get_dataset(lags)\n",
    "\n",
    "sample = next(iter(dataset))\n",
    "num_nodes = sample.x.size(0)\n",
    "\n",
    "train_dataset, test_dataset = temporal_signal_split(dataset, train_ratio=0.4)\n",
    "\n",
    "train_loader = DataLoader(list(train_dataset), batch_size=batch_size, shuffle=True)\n",
    "test_loader = DataLoader(list(test_dataset), batch_size=batch_size, shuffle=False)\n",
    "\n",
    "### MODEL DEFINITION\n",
    "class AttentionGCN(nn.Module):\n",
    "    def __init__(self):\n",
    "        super(AttentionGCN, self).__init__()\n",
    "\n",
    "        residual_channels = 32\n",
    "        skip_channels = 32\n",
    "        end_channels = 32\n",
    "        latent_dim = 32\n",
    "\n",
    "        self.encoder = nn.Sequential(\n",
    "            MTGNN(\n",
    "                gcn_true=True,\n",
    "                build_adj=True,\n",
    "                gcn_depth=2,\n",
    "                num_nodes=num_nodes,\n",
    "                kernel_set=[5,5,5],\n",
    "                kernel_size=5,\n",
    "                dropout=0.2,\n",
    "                subgraph_size=15,\n",
    "                node_dim=64,\n",
    "                dilation_exponential=3,\n",
    "                conv_channels=9,\n",
    "                residual_channels=residual_channels,\n",
    "                skip_channels=skip_channels,\n",
    "                end_channels=end_channels,\n",
    "                seq_length=lags,\n",
    "                in_dim=1,\n",
    "                out_dim=latent_dim,\n",
    "                layers=3,\n",
    "                propalpha=0.4,\n",
    "                tanhalpha=1,\n",
    "                layer_norm_affline=True,\n",
    "                xd=None\n",
    "            ),\n",
    "            nn.BatchNorm2d(latent_dim),\n",
    "            nn.LeakyReLU()\n",
    "        )\n",
    "\n",
    "        self.prediction = nn.Sequential(\n",
    "            nn.Conv2d(\n",
    "                in_channels=latent_dim,\n",
    "                out_channels=32,\n",
    "                kernel_size=(1, 1),\n",
    "                bias=True,\n",
    "            ), \n",
    "            nn.BatchNorm2d(32),\n",
    "            nn.ReLU(),\n",
    "            nn.Conv2d(\n",
    "                in_channels=32,\n",
    "                out_channels=32,\n",
    "                kernel_size=(1, 1),\n",
    "                bias=True,\n",
    "            ),\n",
    "            nn.BatchNorm2d(32),\n",
    "            nn.ReLU(),\n",
    "            nn.Conv2d(\n",
    "                in_channels=32,\n",
    "                out_channels=1,\n",
    "                kernel_size=(1, 1),\n",
    "                bias=True,\n",
    "            )\n",
    "        )\n",
    "\n",
    "        self.reconstruction = nn.Sequential(\n",
    "            nn.Conv2d(\n",
    "                in_channels=latent_dim,\n",
    "                out_channels=64,\n",
    "                kernel_size=(1, 1),\n",
    "                bias=True,\n",
    "            ),\n",
    "            nn.BatchNorm2d(64),\n",
    "            nn.ReLU(),\n",
    "            nn.Conv2d(\n",
    "                in_channels=64,\n",
    "                out_channels=64,\n",
    "                kernel_size=(1, 1),\n",
    "                bias=True,\n",
    "            ),\n",
    "            nn.BatchNorm2d(64),\n",
    "            nn.ReLU(),\n",
    "            nn.Conv2d(\n",
    "                in_channels=64,\n",
    "                out_channels=lags,\n",
    "                kernel_size=(1, 1),\n",
    "                bias=True,\n",
    "            )\n",
    "        )\n",
    "\n",
    "    def forward(self, window):\n",
    "        \n",
    "        x = window.x.view(-1, 1, num_nodes, lags) # (batch, 1, lags, num_nodes)      \n",
    "        h = self.encoder(x) # (batch, out_channels, num_nodes, 1)\n",
    "        \n",
    "        y_hat = self.prediction(h).squeeze(-1).permute(0,2,1).flatten()\n",
    "        x_hat = self.reconstruction(h).squeeze(-1).permute(0,2,1).contiguous().view(-1, lags)\n",
    "        \n",
    "        return y_hat, x_hat\n",
    "\n",
    "model = AttentionGCN()\n",
    "optimizer = torch.optim.Adam(model.parameters(), lr=0.01)\n",
    "\n",
    "### TRAIN\n",
    "model.train()\n",
    "nn.MSELoss\n",
    "pred_loss_history = []\n",
    "rec_loss_history = []\n",
    "for _ in tqdm(range(epochs)):\n",
    "    total_pred_loss = 0\n",
    "    total_rec_loss = 0\n",
    "    for i, window in enumerate(train_loader):\n",
    "        optimizer.zero_grad()\n",
    "        y_pred, x_rec = model(window)\n",
    "\n",
    "        assert y_pred.shape == window.y.shape\n",
    "        assert x_rec.shape == window.x.shape \n",
    "\n",
    "        pred_loss = torch.mean((y_pred - window.y)**2)\n",
    "        rec_loss = torch.mean((x_rec - window.x)**2)\n",
    "        \n",
    "        loss = pred_loss + rec_loss\n",
    "        loss.backward()\n",
    "        optimizer.step()\n",
    "\n",
    "        total_rec_loss += rec_loss.item()\n",
    "        total_pred_loss += pred_loss.item()\n",
    "\n",
    "    total_rec_loss /= i+1\n",
    "    total_pred_loss /= i+1\n",
    "\n",
    "    pred_loss_history.append(total_pred_loss)\n",
    "    rec_loss_history.append(total_rec_loss)\n",
    "\n",
    "### TEST \n",
    "model.eval()\n",
    "total_pred_loss = 0\n",
    "total_rec_loss = 0\n",
    "for i, window in enumerate(train_loader):\n",
    "    optimizer.zero_grad()\n",
    "    y_pred, x_rec = model(window)\n",
    "\n",
    "    assert y_pred.shape == window.y.shape\n",
    "    assert x_rec.shape == window.x.shape \n",
    "\n",
    "    pred_loss = torch.mean((y_pred - window.y)**2)\n",
    "    rec_loss = torch.mean((x_rec - window.x)**2)\n",
    "    \n",
    "    loss = pred_loss + rec_loss\n",
    "    loss.backward()\n",
    "    optimizer.step()\n",
    "\n",
    "    total_rec_loss += rec_loss.item()\n",
    "    total_pred_loss += pred_loss.item()\n",
    "\n",
    "total_rec_loss /= i+1\n",
    "total_pred_loss /= i+1\n",
    "\n",
    "\n",
    "### RESULTS PLOT\n",
    "colors = ['#2300a8', '#8400a8'] # '#8400a8', '#00A658'\n",
    "plot_dict = {'Prediction': (pred_loss_history, colors[0]), 'Reconstruction': (rec_loss_history, colors[1])}\n",
    "\n",
    "n = len(pred_loss_history)\n",
    "\n",
    "# plot train and val losses and fill area under the curve\n",
    "fig, ax = plt.subplots()\n",
    "x_axis = list(range(1, n+1))\n",
    "for key, (data, color) in plot_dict.items():\n",
    "    ax.plot(x_axis, data, \n",
    "                label=key, \n",
    "                linewidth=2, \n",
    "                linestyle='-', \n",
    "                alpha=1, \n",
    "                color=color)\n",
    "    ax.fill_between(x_axis, data, \n",
    "                alpha=0.3, \n",
    "                color=color)\n",
    "\n",
    "# figure labels\n",
    "ax.set_title('Loss over time', fontweight='bold')\n",
    "ax.set_xlabel('Epochs', fontweight='bold')\n",
    "ax.set_ylabel('Mean Squared Error', fontweight='bold')\n",
    "ax.legend(loc='upper right')\n",
    "\n",
    "# remove top and right borders\n",
    "ax.spines['top'].set_visible(False)\n",
    "ax.spines['right'].set_visible(False)\n",
    "\n",
    "# adds major gridlines\n",
    "ax.grid(color='grey', linestyle='-', linewidth=0.35, alpha=0.8)\n",
    "\n",
    "# y-axis in log scale (reconstruction loss tends to start high)\n",
    "ax.set_yscale('log')\n",
    "plt.show()\n",
    "\n",
    "print(\"Train MSE: Prediction: {:.4f} - Reconstruction: {:.4f}\".format(pred_loss_history[-1], rec_loss_history[-1]))\n",
    "print(\"Test MSE: Prediction: {:.4f} - Reconstruction: {:.4f}\".format(total_pred_loss, total_rec_loss))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f911f77b",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
